
/* scrolling.h */

#ifndef SCROLLING_H
#define SCROLLING_H

#include "data/bg_star_data.h"
#include "data/bg_planet_data.h"
#include "data/bg_palette_data.h"

#include "input.h"
#include "globals.h"
#include "timer.h"


////////////////////////////////////////////////////////////
// function prototypes
////////////////////////////////////////////////////////////
void InitializeScrollingBackground(void);
void LoadContentForScrollingBackground(void);
void UpdateScrollingBackground(void);
void UpdateLoadedMemoryForStarBackground(void);
void UpdateLoadedMemoryForPlanetBackground(void);

void IncrementBgX(int);
void IncrementBgY(int);
void DecrementBgX(int);
void DecrementBgY(int);

void DrawScrollingBackground(void);
void CopyStarBgColumn(int, int, int, int);
void CopyStarBgRow(int, int, int, int);
void CopyPlanetBgColumn(int, int, int, int);
void CopyPlanetBgRow(int, int, int, int);


////////////////////////////////////////////////////////////
// defines
////////////////////////////////////////////////////////////

#define MAX_SCROLL_X 1807
#define MAX_SCROLL_Y 1887

#define STAR_MAP_WIDTH 256
#define STAR_MAP_HEIGHT 256

#define STAR_MAP_WIDTH_PIXELS 	STAR_MAP_WIDTH 	* 8
#define STAR_MAP_HEIGHT_PIXELS 	STAR_MAP_HEIGHT * 8

#define PLANET_MAP_WIDTH 128
#define PLANET_MAP_HEIGHT 128

#define BGPaletteMem ((unsigned short*)0x5000000)

// background mode identifiers
#define BG0_ENABLE 0x100
#define BG1_ENABLE 0x200
#define BG2_ENABLE 0x400
#define BG3_ENABLE 0x800

// scrolling registers for backgrounds
#define REG_BG0HOFS *(volatile unsigned short*)0x4000010
#define REG_BG0VOFS *(volatile unsigned short*)0x4000012
#define REG_BG1HOFS *(volatile unsigned short*)0x4000014
#define REG_BG1VOFS *(volatile unsigned short*)0x4000016
#define REG_BG2HOFS *(volatile unsigned short*)0x4000018
#define REG_BG2VOFS *(volatile unsigned short*)0x400001A
#define REG_BG3HOFS *(volatile unsigned short*)0x400001C
#define REG_BG3VOFS *(volatile unsigned short*)0x400001E

// background setup registers and data
#define REG_BG0CNT *(volatile unsigned short*)0x4000008
#define REG_BG1CNT *(volatile unsigned short*)0x400000A
#define REG_BG2CNT *(volatile unsigned short*)0x400000C
#define REG_BG3CNT *(volatile unsigned short*)0x400000E
#define BG_COLOR256 0x80
#define CHAR_SHIFT 2
#define SCREEN_SHIFT 8
#define WRAPAROUND 0x1

// palette for Backgrounds
#define BGPaletteMem ((unsigned short*)0x5000000)

// background tile bitmap sizes
#define TEXTBG_SIZE_256x256 0x0
#define TEXTBG_SIZE_256x512	0x8000
#define TEXTBG_SIZE_512x256	0x4000
#define TEXTBG_SIZE_512x512	0xC000

// background memory offset macros
#define CharBaseBlock(n) (((n)*0x4000)+0x6000000)
#define ScreenBaseBlock(n) (((n)*0x800)+0x6000000)


////////////////////////////////////////////////////////////
// structs
////////////////////////////////////////////////////////////

typedef struct BgInfo {
    int x, y;
    int dx, dy;
    // abs ('absolute') values refer to the whole 1024x1024 map
	int absNextCol, absPrevCol;
	int absNextRow, absPrevRow;
	// rel ('relative') values refer to the viewable 256x256 map
	int relNextCol, relPrevCol;
	int relNextRow, relPrevRow;
} BgInfo;


////////////////////////////////////////////////////////////
// global variables
////////////////////////////////////////////////////////////

unsigned short* bg0map = (unsigned short*)ScreenBaseBlock(31);
unsigned short* bg1map = (unsigned short*)ScreenBaseBlock(30);
BgInfo starsBG, planetsBG;

BOOL BgCanScrollLeft, BgCanScrollRight, BgCanScrollUp, BgCanScrollDown;


////////////////////////////////////////////////////////////
// Function: InitializeScrollingBackground
// one time stuff for scrolling background; initializes (does not create) variables to be used
////////////////////////////////////////////////////////////
void InitializeScrollingBackground()
{
    // set up background 0 place in ScreenBaseBlock 31
	REG_BG0CNT = 0 | (0 << CHAR_SHIFT) | BG_COLOR256 | TEXTBG_SIZE_256x256 | (31 << SCREEN_SHIFT) | WRAPAROUND;
	// set up background 1 place in ScreenBaseBlock 30
	REG_BG1CNT =  3 | (1 << CHAR_SHIFT) | BG_COLOR256 | TEXTBG_SIZE_256x256 | (30 << SCREEN_SHIFT) | WRAPAROUND;
	
	// background - terrain
	starsBG.x = 8, starsBG.y = 8;
	starsBG.dx = 0, starsBG.dy = 0;
	starsBG.absNextCol = 32, starsBG.absPrevCol = -1;
	starsBG.absNextRow = 32, starsBG.absPrevRow = -1;
	starsBG.relNextCol = 0, starsBG.relPrevCol = 31;
	starsBG.relNextRow = 0, starsBG.relPrevRow = 31;
	
	// planetsBG - "clouds"	
	planetsBG.x = 8, planetsBG.y = 8;
	planetsBG.dx = 0, planetsBG.dy = 0;
	planetsBG.absNextCol = 32, planetsBG.absPrevCol = -1;
	planetsBG.absNextRow = 32, planetsBG.absPrevRow = -1;
	planetsBG.relNextCol = 0, planetsBG.relPrevCol = 31;
	planetsBG.relNextRow = 0, planetsBG.relPrevRow = 31;
	
	// set background scroll registers
	REG_BG0VOFS = starsBG.x;
	REG_BG0HOFS = starsBG.y;
	
	// set planetsBG scroll registers
	REG_BG1VOFS = planetsBG.y;
	REG_BG1HOFS = planetsBG.x;
	
	BgCanScrollLeft = TRUE;
	BgCanScrollRight = TRUE;
	BgCanScrollUp = TRUE;
	BgCanScrollDown = TRUE;
}


////////////////////////////////////////////////////////////
// Function: LoadContentForScrollingBackground
// one time stuff for scrolling background; copies content into proper memory locations
////////////////////////////////////////////////////////////
void LoadContentForScrollingBackground()
{
    int i;

	// copy the palette into the background palette memory
	DMAFastCopy((void*)bg_palette, (void*)BGPaletteMem, 256, DMA_16NOW);

	// copy the tile images into the tile memory
	DMAFastCopy((void*)bg_star_tiles, (void*)CharBaseBlock(0), 1728/4, DMA_32NOW);
	DMAFastCopy((void*)bg_planet_tiles, (void*)CharBaseBlock(1), 1664/4, DMA_32NOW);

    // load background map into bg0map
	for (i = 0; i < 32; i++){
		CopyStarBgColumn(starsBG.relPrevCol + 1, i, starsBG.absPrevCol + 1, i);
	}
	
    // load planetsBG map into bg1map	
	for(i = 0; i < 32; i++) {
		CopyPlanetBgColumn(planetsBG.relPrevCol + 1, i, planetsBG.absPrevCol + 1, i);
	}
}


// In milliseconds
int time_since_last_move = 0;     // total so far
int const time_for_move = 10;      // time we want to elapsed before next screen movement

////////////////////////////////////////////////////////////
// Function: UpdateScrollingBackground
// update loop for scrolling background; responsible for making all necessary changes to variable values and states
////////////////////////////////////////////////////////////
void UpdateScrollingBackground()
{
    UpdateLoadedMemoryForStarBackground();
    UpdateLoadedMemoryForPlanetBackground();
    
    if (starsBG.x > 0) {
        BgCanScrollLeft = TRUE;
    }
    else {
        BgCanScrollLeft = FALSE;
    }
    
    if (starsBG.x < MAX_SCROLL_X) {
        BgCanScrollRight = TRUE;
    }
    else {
        BgCanScrollRight = FALSE;
    }
    
    if (starsBG.y > 0) {
        BgCanScrollUp = TRUE;
    }
    else {
        BgCanScrollUp = FALSE;
    }

    if (starsBG.y < MAX_SCROLL_Y) {
        BgCanScrollDown = TRUE;
    }
    else {
        BgCanScrollDown = FALSE;
    }
}


////////////////////////////////////////////////////////////
// Function: UpdateLoadedMemoryForBackground
// checks if new column or row needs to be copied into bg0map for main terrain
////////////////////////////////////////////////////////////
void UpdateLoadedMemoryForStarBackground() {
    if (starsBG.dx > 7) {
        starsBG.dx = 0;
		CopyStarBgColumn(starsBG.relPrevRow + 1, starsBG.relNextCol, starsBG.absPrevRow + 1, starsBG.absNextCol++);
		starsBG.absPrevCol++;
		starsBG.relNextCol = (starsBG.relNextCol + 1) % 32;
		starsBG.relPrevCol = (starsBG.relPrevCol + 1) % 32;
	}
	if (starsBG.dx < -7) {
		starsBG.dx = 0;
		CopyStarBgColumn(starsBG.relPrevRow + 1, starsBG.relPrevCol, starsBG.absPrevRow + 1, starsBG.absPrevCol--);
		starsBG.absNextCol--;
		
		if (starsBG.relPrevCol <= 0) {
			starsBG.relPrevCol = 31;
		}
		else {
		    starsBG.relPrevCol--;
        }

		if (starsBG.relNextCol <= 0) {
		    starsBG.relNextCol = 31;
        }
		else {
		    starsBG.relNextCol--;
        }
	}
    if (starsBG.dy > 7) {
		starsBG.dy = 0;
		CopyStarBgRow(starsBG.relPrevCol + 1, starsBG.relNextRow, starsBG.absPrevCol + 1, starsBG.absNextRow++);
		starsBG.absPrevRow++;
		starsBG.relNextRow = (starsBG.relNextRow + 1) % 32;
		starsBG.relPrevRow = (starsBG.relPrevRow + 1) % 32;
	}
	if (starsBG.dy < -7) {
		starsBG.dy = 0;
		CopyStarBgRow(starsBG.relPrevCol + 1, starsBG.relPrevRow, starsBG.absPrevCol + 1, starsBG.absPrevRow--);
		starsBG.absNextRow--;
		
        if (starsBG.relPrevRow <= 0) {
			starsBG.relPrevRow = 31;
		}
		else {
		    starsBG.relPrevRow--;
        }

		if (starsBG.relNextRow <= 0) {
		    starsBG.relNextRow = 31;
        }
		else {
		    starsBG.relNextRow--;
        }
	}
}


////////////////////////////////////////////////////////////
// Function: UpdateLoadedMemoryForForeground
// checks if new column or row needs to be copied into bg1map for planetsBG
////////////////////////////////////////////////////////////
void UpdateLoadedMemoryForPlanetBackground() {
    if (planetsBG.dx > 7) {
    	planetsBG.dx = 0;
    	CopyPlanetBgColumn(planetsBG.relPrevRow + 1, planetsBG.relNextCol, planetsBG.absPrevRow + 1, planetsBG.absNextCol++);
    	planetsBG.absPrevCol++;
    	planetsBG.relNextCol = (planetsBG.relNextCol + 1) % 32;
    	planetsBG.relPrevCol = (planetsBG.relPrevCol + 1) % 32;
    }
    if (planetsBG.dx < -7) {
    	planetsBG.dx = 0;
    	CopyPlanetBgColumn(planetsBG.relPrevRow + 1, planetsBG.relPrevCol, planetsBG.absPrevRow + 1, planetsBG.absPrevCol--);
    	planetsBG.absNextCol--;
    	
    	if (planetsBG.relPrevCol <= 0) {
    		planetsBG.relPrevCol = 31;
		}
    	else {
    	    planetsBG.relPrevCol--;
        }

    	if (planetsBG.relNextCol <= 0) {
    	    planetsBG.relNextCol = 31;
	    }
    	else {
    	    planetsBG.relNextCol--;
	    }
    }
    if (planetsBG.dy > 7) {
    	planetsBG.dy = 0;
    	CopyPlanetBgRow(planetsBG.relPrevCol + 1, planetsBG.relNextRow, planetsBG.absPrevCol + 1, planetsBG.absNextRow++);
    	planetsBG.absPrevRow++;
    	planetsBG.relNextRow = (planetsBG.relNextRow + 1) % 32;
    	planetsBG.relPrevRow = (planetsBG.relPrevRow + 1) % 32;
    }
    if (planetsBG.dy < -7) {
    	planetsBG.dy = 0;
    	CopyPlanetBgRow(planetsBG.relPrevCol + 1, planetsBG.relPrevRow, planetsBG.absPrevCol + 1, planetsBG.absPrevRow--);
    	planetsBG.absNextRow--;
    		
    	if (planetsBG.relPrevRow <= 0) {
    		planetsBG.relPrevRow = 31;
		}
    	else {
    	    planetsBG.relPrevRow--;
	    }

    	if (planetsBG.relNextRow <= 0) {
    	    planetsBG.relNextRow = 31;
	    }
    	else {
    	    planetsBG.relNextRow--;
	    }
    }
}


////////////////////////////////////////////////////////////
// Function: IncrementBgX
// ...
////////////////////////////////////////////////////////////
void IncrementBgX(int x)
{
    int i;
    
    for (i = 0; i < x; i++) {
        if (BgCanScrollRight) {
        	starsBG.x++;
        	starsBG.dx++;

        	if (starsBG.x % 4 == 0) {
                planetsBG.x++;
                planetsBG.dx++;
            }
        }
    }
}


////////////////////////////////////////////////////////////
// Function: IncrementBgY
// ...
////////////////////////////////////////////////////////////
void IncrementBgY(int x)
{	
    int i;

    for (i = 0; i < x; i++) {
        if (BgCanScrollDown) {
        	starsBG.y++;
        	starsBG.dy++;

        	if (starsBG.y % 4 == 0) {
                planetsBG.y++;
                planetsBG.dy++;
            }
        }
    }
}


////////////////////////////////////////////////////////////
// Function: DecrementBgX
// ...
////////////////////////////////////////////////////////////
void DecrementBgX(int x)
{
    int i;

    for (i = 0; i < x; i++) {
        if (BgCanScrollLeft) {
        	starsBG.x--;
        	starsBG.dx--;
        	
        	if (starsBG.x % 4 == 0) {
                planetsBG.x--;
                planetsBG.dx--;
            }
        }
    }
}


////////////////////////////////////////////////////////////
// Function: DecrementBgY
// ...
////////////////////////////////////////////////////////////
void DecrementBgY(int x)
{
    int i;

    for (i = 0; i < x; i++) {
        if (BgCanScrollUp) {
        	starsBG.y--;
        	starsBG.dy--;

        	if (starsBG.y % 4 == 0) {
                planetsBG.y--;
                planetsBG.dy--;
            }
        }
    }
}


////////////////////////////////////////////////////////////
// Function: DrawScrollingBackground
// updates background scroll registers
////////////////////////////////////////////////////////////
void DrawScrollingBackground()
{	
	// update background 0 scrolling registers for main terrain
	REG_BG0HOFS = starsBG.x;
	REG_BG0VOFS = starsBG.y;
	
	// update background 1 scrolling registers for planetsBG
	REG_BG1HOFS = planetsBG.x;
	REG_BG1VOFS = planetsBG.y;
}


////////////////////////////////////////////////////////////
// Function: CopyStarBgColumn
// viewableMapCol: the column in our 256x256 background to copy to
// wholeMapColumn: the column from the background map we would like to copy from
// this function copies a column from bg_Map into bg0map
////////////////////////////////////////////////////////////
void CopyStarBgColumn(int viewableMapYOffset, int viewableMapColumn, int wholeMapYOffset, int wholeMapColumn)
{
	int i;
	
	// bg0map is the 256x256 map loaded into memory
	// bg_Map is our map data consisting of the whole 1024x1024 map
	for (i = 0; i < 32; i++) {
		bg0map[viewableMapColumn + ((i + viewableMapYOffset) % 32) * 32] = bg_star_map[wholeMapColumn + (STAR_MAP_WIDTH * (i + wholeMapYOffset))];
	}
}


////////////////////////////////////////////////////////////
// Function: CopyStarBgRow
// viewableMapRow: the row in our 256x256 background to copy to
// wholeMapRow: the row from the background map we would like to copy from
////////////////////////////////////////////////////////////
void CopyStarBgRow(int viewableMapXOffset, int viewableMapRow, int wholeMapXOffset, int wholeMapRow)
{
    // TODO: use fast copy instead of for loop

	int i;
	for(i = 0; i < 32; i++) {
		bg0map[((i + viewableMapXOffset) % 32) + viewableMapRow * 32] = bg_star_map[(wholeMapXOffset + i) + (STAR_MAP_WIDTH * wholeMapRow)];
	}
}


////////////////////////////////////////////////////////////
// Function: CopyPlanetBgColumn
// viewableMapRow: the row in our 256x256 background to copy to
// wholeMapRow: the row from the background map we would like to copy from
////////////////////////////////////////////////////////////
void CopyPlanetBgColumn(int viewableMapYOffset, int viewableMapColumn, int wholeMapYOffset, int wholeMapColumn)
{
	int i;
	for(i = 0; i < 32; i++) {
		bg1map[viewableMapColumn % 32 + ((i + viewableMapYOffset) % 32) * 32] = bg_planet_map[wholeMapColumn + (PLANET_MAP_WIDTH * (i + wholeMapYOffset))];
	}
}


////////////////////////////////////////////////////////////
// Function: CopyPlanetBgRow
// viewableMapRow: the row in our 256x256 background to copy to
// wholeMapRow: the row from the background map we would like to copy from
////////////////////////////////////////////////////////////
void CopyPlanetBgRow(int viewableMapXOffset, int viewableMapRow, int wholeMapXOffset, int wholeMapRow)
{
    // TODO: use fast copy instead of for loop

	int i;
	for(i = 0; i < 32; i++) {
		bg1map[((i + viewableMapXOffset) % 32) + viewableMapRow * 32] = bg_planet_map[(wholeMapXOffset + i) + (PLANET_MAP_WIDTH * wholeMapRow)];
	}
}

#endif

